人類溝通需要技巧,程式語言靠的是方法。
我們除了可以透過 Props 來讓父元件傳值給子元件外,也可以透過子元件向父元件傳遞訊息,並可以直接在子元件中操控父元件裡的資料。
在父元件傳值給子元件所使用的 Props ,在傳遞資料時是單向資料流(One-Way Data Flow),也就是說,只允許子元件向父元件傳遞資料,而不允許子元件直接操控 Props 裡的資料。有這個原則的原因是因為如果直接讓子元件直接操控 Props 裡的資料,資料的控制邏輯會變的很複雜與雜亂。單向資料流還是比較容易理解,Vue 也較推薦單向資料流的用法。雖然可以做到雙向但 Vue 不建議使用雙向。
那麼在 Vue 如何透過自定義事件向父元件傳遞訊息呢?即是使用 Vue 的內建$emit()方法。
子元件透過自定義事件向父元件傳遞訊息
語法:<button v-on:click="$emit('enlarge-text')>Enlarge Text</button>
說明:一個子元件裡的按鈕,點擊時會觸發一個特定的方法$emit(),並帶上一個參數,此參數為自定義的事件名稱enlarge-text,這個事件會傳遞給父元件,所以父元件會去監聽這個事件。
父元件中監聽子元件的事件
語法:<menu-item v-on:enlarge-text="fontSize += 0.1"></menu-item>
說明:v-on:enlarge-text為綁定事件名稱,fontSize += 0.1則為事件對應的處理邏輯。
透過實作的情境會比較清楚傳遞的運作原理:
實作範例效果:在子元件中點擊Enlarge Text按鈕之後,會控制父元件中的parentsMsg使文字有大小變化。
template 插入我們在data的屬性parentsMsg內容為'Hello, is parentsMsg',為控制目標。template裡加上按鈕綁定監聽事件,並以$emit()方法,帶上指定的自定義事件參數。template 綁定剛剛的自定義事件參數methods增加方法handleSize,每按一次按鈕增加字體大小 5px,並在 data 裡增加屬性fontSize: 12,預設值。template 的插入值parentsMsg 元素上綁定元素的字體大小,記得要加入字體單位px
<!-- 父元件的HTML -->
<div id="app">
  <menu-item @enlarge-text="handleBiggerSize"></menu-item>
</div>
<script>
// 子元件 使用$emit()傳遞
Vue.component('menu-item',{
  template: `
    <div>
      <div >{{parentsMsg}}</div>
      <button @click="$emit('enlarge-text')">放大父元件中元素的字體大小</button>
    </div>
  `
})
// 父元件
const vm= new Vue({
  el:'#app',
  data:{
    parentsMsg: 'Hello, is parentsMsg',
    fontSize: 16,
  },
  methods:{
    handleBiggerSize: function(){
      // 字體變大
      this.fontSize += 5
    },
  }
})
</script>
我們也可以直接在$emit()裡以第二個參數帶值進去,就不需要每次去父元件的方法裡修改。
子元件透過自定義事件向父元件傳遞訊息+帶值
語法:<button v-on:click="$emit('enlarge-text',0.1)>Enlarge Text</button>
說明:一個子元件裡的按鈕,點擊時會觸發一個特定的方法$emit(),並帶上兩個參數,第一參數為自定義的事件名稱enlarge-text,第二參數是值,個事件和值會直接傳給父元件,所以父元件會去監聽這個事件。
父元件中監聽子元件的事件
接收語法:<menu-item v-on:enlarge-text+= $event"></menu-item>
說明:v-on:enlarge-text為綁定事件名稱,fontSize += $event則為事件對應的處理邏輯會對應到子主件的綁定方法的第二參數值,$event是固定的寫法。
當按下子元件的模版template裡的按鈕,會直接取到第二參數作為放大字體的增加單位。
實作範例效果:
<!-- 父元件的HTML -->
<div id="app">
  <menu-item @minify-text="handleMinifySize($event)"></menu-item>
</div>
<script>
// 子元件 使用$emit()傳遞
Vue.component('menu-item',{
  template: `
    <div>
      <div >{{parentsMsg}}</div>
      <button @click="$emit('minify-text',5)">縮小父元件中元素的字體大小</button>
    </div>
  `
})
// 父元件
const vm= new Vue({
  el:'#app',
  data:{
    parentsMsg: 'Hello, is parentsMsg',
    fontSize: 16,
  },
  methods:{
    handleMinifySize: function(value){
      // 字體變大
      this.fontSize -= value
    }
  }
})
</script>
每日一句法文有益身心:Je suis fatigué ! --> 者.雖.發踢給! --> 我好累啊!